<?php

namespace api\sms\driver;

class Tencent extends Sms
{
    // 错误码
    private array $errorCode = [
        'FailedOperation.ContainSensitiveWord' => '短信内容中含有敏感词',
        'FailedOperation.FailResolvePacket' => '请求包解析失败',
        'FailedOperation.InsufficientBalanceInSmsPackage' => '套餐包余量不足',
        'FailedOperation.JsonParseFail' => '解析请求包体时候失败',
        'FailedOperation.MarketingSendTimeConstraint' => '营销短信发送时间限制',
        'FailedOperation.PhoneNumberInBlacklist' => '手机号在免打扰名单库中',
        'FailedOperation.SignatureIncorrectOrUnapproved' => '签名未审批或格式错误',
        'FailedOperation.TemplateIncorrectOrUnapproved' => '模板未审批或内容不匹配',
        'FailedOperation.TemplateParamSetNotMatchApprovedTemplate' => '请求内容与审核通过的模板内容不匹配',
        'FailedOperation.TemplateUnapprovedOrNotExist' => '模板未审批或不存在',
        'InternalError.OtherError' => '其他错误',
        'InternalError.RequestTimeException' => '请求发起时间不正常',
        'InternalError.RestApiInterfaceNotExist' => '不存在该 RESTAPI 接口',
        'InternalError.SendAndRecvFail' => '接口超时或短信收发包超时',
        'InternalError.SigFieldMissing' => '后端包体中请求包体没有 Sig 字段或 Sig 为空',
        'InternalError.SigVerificationFail' => '后端校验 Sig 失败',
        'InternalError.Timeout' => '请求下发短信超时',
        'InternalError.UnknownError' => '未知错误类型',
        'InvalidParameterValue.ContentLengthLimit' => '请求的短信内容太长',
        'InvalidParameterValue.IncorrectPhoneNumber' => '手机号格式错误',
        'InvalidParameterValue.ProhibitedUseUrlInTemplateParameter' => '禁止在模板变量中使用 URL',
        'InvalidParameterValue.SdkAppIdNotExist' => 'SdkAppId 不存在',
        'InvalidParameterValue.TemplateParameterFormatError' => '验证码模板参数格式错误',
        'InvalidParameterValue.TemplateParameterLengthLimit' => '单个模板变量字符数超过12个',
        'LimitExceeded.AppCountryOrRegionDailyLimit' => '业务短信国家/地区日下发条数超过设定的上限',
        'LimitExceeded.AppCountryOrRegionInBlacklist' => '业务短信国家/地区不在国际港澳台短信发送限制设置的列表中而禁发',
        'LimitExceeded.AppDailyLimit' => '业务短信日下发条数超过设定的上限',
        'LimitExceeded.AppGlobalDailyLimit' => '业务短信国际/港澳台日下发条数超过设定的上限',
        'LimitExceeded.AppMainlandChinaDailyLimit' => '业务短信中国大陆日下发条数超过设定的上限',
        'LimitExceeded.DailyLimit' => '短信日下发条数超过设定的上限 (国际/港澳台)',
        'LimitExceeded.DeliveryFrequencyLimit' => '下发短信命中了频率限制策略',
        'LimitExceeded.PhoneNumberCountLimit' => '调用接口单次提交的手机号个数超过200个',
        'LimitExceeded.PhoneNumberDailyLimit' => '单个手机号日下发短信条数超过设定的上限',
        'LimitExceeded.PhoneNumberOneHourLimit' => '单个手机号1小时内下发短信条数超过设定的上限',
        'LimitExceeded.PhoneNumberSameContentDailyLimit' => '单个手机号下发相同内容超过设定的上限',
        'LimitExceeded.PhoneNumberThirtySecondLimit' => '单个手机号30秒内下发短信条数超过设定的上限',
        'MissingParameter.EmptyPhoneNumberSet' => '传入的号码列表为空',
        'UnauthorizedOperation.IndividualUserMarketingSmsPermissionDeny' => '个人用户没有发营销短信的权限',
        'UnauthorizedOperation.RequestIpNotInWhitelist' => '请求 IP 不在白名单中',
        'UnauthorizedOperation.RequestPermissionDeny' => '请求没有权限',
        'UnauthorizedOperation.SdkAppIdIsDisabled' => '此 SdkAppId 禁止提供服务',
        'UnauthorizedOperation.SerivceSuspendDueToArrears' => '欠费被停止服务',
        'UnauthorizedOperation.SmsSdkAppIdVerifyFail' => 'SmsSdkAppId 校验失败',
        'UnauthorizedOperation.' => '不支持该请求',
        'UnauthorizedOperation.ChineseMainlandTemplateToGlobalPhone' => '国内短信模板不支持发送国际/港澳台手机号',
        'UnauthorizedOperation.ContainDomesticAndInternationalPhoneNumber' => '群发请求里既有国内手机号也有国际手机号',
        'UnauthorizedOperation.GlobalTemplateToChineseMainlandPhone' => '国际/港澳台短信模板不支持发送国内手机号',
        'UnauthorizedOperation.UnsuportedRegion' => '不支持该地区短信下发',
    ];


    /**
     * 发送短信
     *
     * @param string $phone 手机号
     * @param array $data 发送数据
     * @return bool
     */
    public function sendSms(string $phone, array $data): bool
    {
        try {
            $this->setContent($data['content']);

            $params = [];
            foreach ($data['params'] as $v) {
                $params[] = (string)$v;
            }

            $result = $this->httpRequest([
                'PhoneNumberSet' => [$phone],
                'SmsSdkAppId' => $this->config['tencent_appid'],
                'SignName' => $this->config['signature'],
                'TemplateId' => $data['template_id'],
                'TemplateParamSet' => $params,
            ]);
            if (isset($result['Response']['SendStatusSet'][0]['Code'])) {
                if ($result['Response']['SendStatusSet'][0]['Code'] !== 'Ok') {
                    $this->setErrorMessage($this->errorCode[$result['Response']['SendStatusSet'][0]['Code']] ?? $result['Response']['SendStatusSet'][0]['Code']);
                } else {
                    $this->setSendStatus(true);
                }
            } else {
                $this->setErrorMessage('未知错误');
            }
        } catch (\Throwable $e) {
            $this->setSendStatus(false);
            $this->setErrorMessage('系统错误 ' . $e->getMessage());
        }
        return $this->getSendStatus();
    }

    /**
     *
     *
     * @param $requestData
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function httpRequest($requestData)
    {
        $secretId = $this->config['tencent_secret_id'] ?? '';
        $secretKey = $this->config['tencent_secret_key'] ?? '';
        $host = "sms.tencentcloudapi.com";
        $service = "sms";
        $version = "2021-01-11";
        $action = "SendSms";
        $region = "ap-guangzhou";
        $timestamp = time();
        $algorithm = "TC3-HMAC-SHA256";

        // step 1: build canonical request string
        $httpRequestMethod = "POST";
        $canonicalUri = "/";
        $canonicalQueryString = "";
        $canonicalHeaders = "content-type:application/json; charset=utf-8\n" . "host:" . $host . "\n";
        $signedHeaders = "content-type;host";

        $payload = json_encode($requestData);
        $hashedRequestPayload = hash("SHA256", $payload);
        $canonicalRequest = $httpRequestMethod . "\n"
            . $canonicalUri . "\n"
            . $canonicalQueryString . "\n"
            . $canonicalHeaders . "\n"
            . $signedHeaders . "\n"
            . $hashedRequestPayload;


        // step 2: build string to sign
        $date = gmdate("Y-m-d", $timestamp);
        $credentialScope = $date . "/" . $service . "/tc3_request";
        $hashedCanonicalRequest = hash("SHA256", $canonicalRequest);
        $stringToSign = $algorithm . "\n"
            . $timestamp . "\n"
            . $credentialScope . "\n"
            . $hashedCanonicalRequest;

        // step 3: sign string
        $secretDate = hash_hmac("SHA256", $date, "TC3" . $secretKey, true);
        $secretService = hash_hmac("SHA256", $service, $secretDate, true);
        $secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true);
        $signature = hash_hmac("SHA256", $stringToSign, $secretSigning);

        // step 4: build authorization
        $authorization = $algorithm
            . " Credential=" . $secretId . "/" . $credentialScope
            . ", SignedHeaders=content-type;host, Signature=" . $signature;

        $client = new \GuzzleHttp\Client();
        $response = $client->request('POST', "https://" . $host, [
            'verify' => app()->getRootPath() . 'cacert.pem',
            'headers' => [
                'Authorization' => $authorization,
                'Content-Type' => 'application/json; charset=utf-8',
                'Host' => $host,
                'X-TC-Action' => $action,
                'X-TC-Timestamp' => $timestamp,
                'X-TC-Version' => $version,
                'X-TC-Region' => $region
            ],
            'json' => $requestData
        ]);

        return json_decode($response->getBody(), true);
    }
}